﻿#include "porting.h"
#include "videoplayer.h"
#include "driver_audio.h"
#include "char_dev.h"
#ifndef _WIN32
#include "yp3_common.h"
#include "yp3_audio_codec_config.h"
#endif

extern void *yp3_volume_init(unsigned short sr, unsigned short ch);
extern void yp3_volume_proc(void *p, uint32_t volume, void *pcm, uint32_t pcm_samples);
extern void yp3_volume_close(void *p);

AVI_PLAYER *g_video_player = 0;

static u8 avi_head(void)
{
	u8 res=0;
	u16 offset=0;
	u32 br=0;

	res=f_read(&g_video_player->favi, (char*)g_video_player->streambuf, AVI_VIDEO_BUF_SIZE, &br);
	if(res)
	{
		Debug_printf(DISP_KEY_FM,"read avi buffer failed: %d.\r\n", res);	
		return 1;
	}

	res=avi_init(g_video_player->streambuf,AVI_VIDEO_BUF_SIZE);
	if(res != AVI_OK){
		Debug_printf(DISP_KEY_FM,"avi_init failed!\r\n");	
		return res;
	}

	if(g_video_player->avix.SampleRate)
	{
		if( g_video_player->avix.Channels != 2){
			Debug_printf(DISP_KEY_FM,"avi channel is not 2!\r\n");	
			return AVI_FORMAT_ERR;
		}
	}

	if( g_video_player->avix.Width != lcddev_width ||
		g_video_player->avix.Height != lcddev_height)
	{
		Debug_printf(DISP_KEY_FM,"avi width and height failed!\r\n");	
		return AVI_FORMAT_ERR;
	}

	offset=avi_srarch_id(g_video_player->streambuf,AVI_VIDEO_BUF_SIZE, (u8 *)"movi");
	f_lseek(&g_video_player->favi,offset+4);
	return res;
}

static void avi_pcm_out(u8 *pcm_data, u32 pcm_chunk)
{
#ifndef _WIN32
	yp3_volume_proc(g_video_player->volume_obj, g_video_player->volume, (void*)pcm_data, pcm_chunk);
#endif
	//pcm_write(g_video_player->hpcm, pcm_data, pcm_chunk);
	//dev_write_audio(g_video_player->hpcm, pcm_data, pcm_chunk,0);
	dev_Write(g_video_player->hpcm ,pcm_data, pcm_chunk,0);
}

static u8 avi_block(void)
{
	u8 res=0;
	u32 br=0;

	if(FR_OK != f_read(&g_video_player->favi, (char*)g_video_player->streambuf, 8, &br)){
		return AVI_STREAM_ERR;
	}

	res = avi_get_streaminfo(g_video_player->streambuf);
	if(res)
	{
		return res;
	}

	if(g_video_player->avix.StreamSize > AVI_VIDEO_BUF_SIZE)
	{
		return AVI_STREAM_ERR;
	}

	if(g_video_player->avix.StreamSize == 0)
		return 0;
	
	if(FR_OK != f_read(&g_video_player->favi, (char*)g_video_player->streambuf,g_video_player->avix.StreamSize,&br)){
		return AVI_STREAM_OVER;
	}

	if(g_video_player->avix.StreamID==AVI_VIDS_FLAG)
	{
		//video
		// uint32_t _start = OAL_get_tick_count();
		res=mjpegdec_decode(g_video_player->streambuf,g_video_player->avix.StreamSize);
		if(res)
		{
			//printf("decode error!\r\n");
		}
		// xprintf("mjpegdec_decode: %d\r\n", OAL_get_tick_count()-_start);
	}
	else if(g_video_player->avix.StreamID==AVI_AUDS_FLAG)
	{
		//audio
		u32 i=0;
		u32 ss = PCM_CHUNK*sizeof(u16)*g_video_player->avix.Channels;
		u32 cnt = g_video_player->avix.StreamSize/ss;
		u32 res_size = (g_video_player->avix.StreamSize - cnt*ss)/(sizeof(u16)*g_video_player->avix.Channels);
		for(i=0; i<cnt; i++)
		{
			avi_pcm_out(&g_video_player->streambuf[i*ss], PCM_CHUNK);
		}

		if(res_size)
		{
			avi_pcm_out(&g_video_player->streambuf[cnt*ss], res_size);
		}
	}
	else
	{
		//printf("unknow tag!\r\n");
	}

	return res;
}

static u8 seek_to_next_movi()
{
	u8 res=0;
	u16 offset=0;
	u32 br=0;
	FSIZE_t start=0;

	while(1)
	{
		start = f_tell(&g_video_player->favi);
		res=f_read(&g_video_player->favi, (char*)g_video_player->streambuf, AVI_VIDEO_BUF_SIZE, &br);
		if(res || (br==0))
		{
			return AVI_STREAM_ERR;
		}
		offset=(u16)avi_srarch_id(g_video_player->streambuf,AVI_VIDEO_BUF_SIZE, (u8 *)"movi");
		if(offset != (u16)(-1))
		{
			f_lseek(&g_video_player->favi,start+offset+4);
			return AVI_OK;
		}
	}
	return AVI_STREAM_ERR;
}

static void* _video_play_avi(void *p)
{
	p = p;
	// f_lseek(&g_video_player->favi,0x3ffdadee);

	while(OAL_FAILED == OAL_sem_wait_timeout(&g_video_player->exit, 0))
	{
		if(g_video_player->pause)
			OAL_sleep(100);
		else{
			u8 ret = avi_block();
			if(ret == AVI_IX00)
			{
				ret = seek_to_next_movi();
			}
			if(ret != AVI_OK)
			{
				//printf("stream over !");
				if(g_video_player->over_func)g_video_player->over_func(VIDEO_PLAYER_OVER);
				break;
			}
		}
	}

	return (void *)0;
}

int8_t video_play_avi(u8 *pname, VIDEO_PLAYER_CALLBACK over_func)
{
	u8 res=0;
	if( g_video_player){
		Debug_printf(DISP_KEY_FM,"video player is alive!\r\n");
		return -1;
	}
	g_video_player = OAL_calloc(1, sizeof(AVI_PLAYER));
	if(!g_video_player){
		Debug_printf(DISP_KEY_FM,"OAL_calloc g_video_player failed!\r\n");
		return -1;
	}
	g_video_player->over_func = over_func;
	memset(&g_video_player->avix, 0, sizeof(AVI_INFO));
	res = f_open(&g_video_player->favi, (char*)pname, FA_READ);
	if(res){
		OAL_free(g_video_player);
		g_video_player = 0;
		Debug_printf(DISP_KEY_FM,"video open file failed!\r\n");
		return res;
	}

	res = avi_head();
	if(res){
		f_close(&g_video_player->favi);
		OAL_free(g_video_player);
		g_video_player = 0;	
		Debug_printf(DISP_KEY_FM,"video avi_head failed!\r\n");	
		return res;
	}

	if (g_video_player->avix.SampleRate)
	{
		struct pcm_config cfg =
		{
			g_video_player->avix.Channels, //2,
			2, //16 bits
			g_video_player->avix.SampleRate, //44100,
			PCM_CHUNK,
			CHUNK_CUT,
			0,
			0
		};
		 
	
		driver_audio_open_opt_t cfg1 = { 0 };
		memcpy(&cfg1.cfg, &cfg, sizeof(struct pcm_config));

		//g_video_player->hpcm = dev_Open_audio0(PCM_OUT, &cfg);
		g_video_player->hpcm = dev_Open("/dev/audio0", 0, &cfg1);

	}
	res = mjpegdec_init((u16)(lcddev_width-g_video_player->avix.Width)/2,(u16)(lcddev_height-g_video_player->avix.Height)/2);
	if(0 == res){
		g_video_player->exit = OAL_sem_create_binary();
		g_video_player->thread = OAL_thread_create(_video_play_avi, 0, "video", OAL_THREAD_PRIORITY_REAL_TIME, 2048);
		return 0;
	}
	return -1;
}

int8_t video_stop_avi()
{
	if(g_video_player){
		OAL_sem_post(&g_video_player->exit);
		OAL_thread_join(g_video_player->thread);
		OAL_sem_destroy(&g_video_player->exit);
		
		mjpegdec_free();
		f_close(&g_video_player->favi);
		
		if(g_video_player->hpcm)
		{
#ifndef _WIN32
			app_audio_switch_pa(0);
#endif
			dev_Close(&g_video_player->hpcm);
		}
#ifndef _WIN32
		yp3_volume_close(g_video_player->volume_obj);
#endif
		OAL_free(g_video_player);
		g_video_player = 0;
	}

	return 0;
}

int8_t video_pause_avi()
{
	if(g_video_player){
		g_video_player->pause = 1;
	}
	return 0;
}

int8_t video_resume_avi()
{
	if(g_video_player){
		g_video_player->pause = 0;
	}
	return 0;	
}

int8_t video_set_volume(uint8_t volume)
{
	if(g_video_player){
		g_video_player->volume = volume;
	}
	return 0;	
}